/**
 * Copyright (C), 2021-2021, Sakura有限公司(尚未成立)
 * FileName: TaosService
 * Author:   Sakura
 * Date:     2021/9/6 20:27
 * Description: 涛思数据的service层
 * History: 修改历史
 * <author>          <time>          <version>          <desc>
 * 作者姓名           修改时间           版本号              描述
 */
package com.casic.ccp.flying.youth.timeseriesdata.service;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.casic.ccp.flying.youth.timeseriesdata.dao.TaosDao;
import com.casic.ccp.flying.youth.timeseriesdata.model.*;
import com.casic.ccp.flying.youth.timeseriesdata.sql.SqlPreparationPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
import java.util.List;

import static com.casic.ccp.flying.youth.timeseriesdata.tool.FileTool.generateUuidFileName;

/**
 * 〈一句话功能简述〉<br>
 * 〈涛思数据的service层〉
 *
 * @author: Sakura
 * @date: 2021/9/6
 */
@Service
@DS("taos")
public class TaosService {

    /**
     * 日志记录器
     */
    private static Logger LOGGER = LoggerFactory.getLogger(TaosService.class);

    @Autowired
    TaosDao taosDao;

    /**
     * 获取一次读取数量
     */
    @Autowired
    Integer capacity;

    @Autowired
    private JdbcService jdbcService;

    @Autowired
    private Long maxDisplayPoint;
    @Autowired
    private SqlPreparationPool sqlPreparationPool;

    /**
     * 数据批量导入
     *
     * @param uploadFile
     * @return DataLoadInfo  当出线异常的时候返回null
     */
    public DataLoadInfo batchDataWrit(UploadFile uploadFile, SocketClient socketClient) {
        // 生成一个随机的表名
        String tableName = generateUuidFileName();
        // 获取文件
        File file = new File(new File(uploadFile.getPath()), uploadFile.getAnotherName() + FileService.SUFFIX);
        // 告诉前端文件大小
        String data = new WebSockeetData(0, file.length()).toString();
        socketClient.sendData(data);
        LOGGER.info("文档大小为: {}",data);
        // 构造一个数据存储记录对象
        DataLoadInfo dataLoadInfo = new DataLoadInfo();
        // 设置相关信息
        dataLoadInfo.setFileName(uploadFile.getName());
        dataLoadInfo.setFileTableId(uploadFile.getId());
        dataLoadInfo.setName(tableName);
        // 文件流
        FileInputStream fin = null;
        try {

            fin = new FileInputStream(file);
            //  获取文件管道
            FileChannel channel = fin.getChannel();
            // NIO 的字节操作
            int capacity = this.capacity * 4;
            // 准备直接对象
            ByteBuffer bf = ByteBuffer.allocate(capacity);
            // 设置小端模式
            bf.order(ByteOrder.LITTLE_ENDIAN);
//            int length = -1;
            long timeMillis = System.currentTimeMillis();
            dataLoadInfo.setStartTime(timeMillis);

            long startTime = System.currentTimeMillis();

            // 创建表
            taosDao.createTable(tableName, uploadFile, timeMillis);

            long endTime = System.currentTimeMillis();
            LOGGER.info("耗时{}毫秒",  (endTime - startTime));
            int read ;
            // 循环读取文件
            while ((read = channel.read(bf))!= -1) {
                // 告诉前端以读取数据大小
                socketClient.sendData(new WebSockeetData(1, (long) read).toString());
                // 转换写出模式
                bf.flip();
                // 准备一个sql语句对象
                SqlPreparation sqlPreparation = null;
                try {
                    sqlPreparation = sqlPreparationPool.borrowObject();
                    sqlPreparation.emptyValues();
                } catch (Exception e) {
                    LOGGER.error("对象池异常", e);
                }
                while (bf.position() < bf.limit()) {
                    sqlPreparation.sequentiallyWriteData(timeMillis++, bf.getFloat());
                }

                // 注意，读取后，将位置置为0，将limit置为容量, 以备下次读入到字节缓冲中，从0开始存储
                bf.clear();
                jdbcService.insertBatchData(sqlPreparation,tableName,socketClient);

            }
            //更新表结束时间标签
            taosDao.updateTableEndTime(tableName, timeMillis);
            dataLoadInfo.setEndTime(timeMillis);
            socketClient.sendData(new WebSockeetData(3, (long) 0L).toString());
            LOGGER.info("文件解析完成!");
            // 处理完成将管道关闭
            channel.close();
        } catch (FileNotFoundException e) {
            LOGGER.error("文件导入失败,无法获取文件对象", e);
            return null;
        } catch (IOException e) {
            LOGGER.error("文件导入失败,文件内容读取异常", e);
            return null;
        } finally {
            if (fin != null) {
                try {
                    // 关闭文件流
                    fin.close();
                } catch (IOException e) {
                    LOGGER.error("文件流关闭异常", e);
                }
            }
        }
        return dataLoadInfo;
    }

    /**
     * 查询方法
     *
     * @param tableName 表名
     * @param type      算法类型
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return
     */
    public List<Data> select(String tableName, AlgorithmType type, Long startTime, Long endTime , Integer windowRatio) {
        // 可有可无,可通过时间计算
        Long count = taosDao.selectValueCount(tableName, startTime, endTime);
        String column = null;
        Long window = 10L;
        // 判断区间数量
        if (maxDisplayPoint >= count) {
            column = "*";
            window = null;
        } else {
            Double valueOf = Double.valueOf(count);
            double v = valueOf / maxDisplayPoint;
            int i = 10;
            if (v >= i) {
                window = Double.valueOf(Math.ceil(v)).longValue();
                window = window * windowRatio;
                LOGGER.info("查询窗口{}毫秒", window);
            }
            switch (type) {
                case AverageValue:
                    column = "AVG(value) as value";
                    break;
                case MinimumValue:
                    column = "MIN(value) as value";
                    break;
                case MaximumValue:
                    column = "MAX(value) as value";
                    break;
                default:
                    column = "AVG(value) as value";
            }
        }
        List<Data> select = taosDao.select(column, tableName, window, startTime, endTime);
        LOGGER.info("查询窗口结果数量{}", select.size());
        return select;
    }

    /**
     * 删除表
     *
     * @param name
     */
    public void delTableByName(String name) {
        taosDao.delTableByName(name);
    }

}
